#ifndef __M_DB_H__
#define __M_DB_H__

#include <mutex>
#include <assert.h>
#include "util.hpp"

class user_table
{
private:
    MYSQL *_mysql;     // 操作句柄
    std::mutex _mutex; // 保护数据库访问
public:
    user_table()
    {
        _mysql = mysql_util::mysql_create();
        assert(_mysql != NULL);
    }

    ~user_table()
    {
        mysql_util::mysql_destroy(_mysql);
        _mysql = NULL;
    }

    // 注册新用户
    bool insert(Json::Value &user)
    {
#define INERT_USER "insert into user values(null, '%s', '%s', 1000, 0, 0);"
        // sprintf(void* buf, char* format, ...)
        if (user["usename"].isNull() || user["password"].isNull())
        {
            DLOG("input usename or password");
            return false;
        }

        char sql[4096] = {0};
        sprintf(sql, INERT_USER, user["usename"].asCString(), user["password"].asCString());
        bool ret = mysql_util::mysql_exec(_mysql, sql);
        if (ret == false)
        {
            DLOG("insert into info failed");
            return false;
        }

        return true;
    }

    // 登录验证，并返回详细的用户信息
    bool login(Json::Value &user)
    {
        if (user["usename"].isNull() || user["password"].isNull())
        {
            DLOG("input usename or password");
            return false;
        }

        // 以用户名和密码作为查询过滤条件，查询数据则表示用户密码一致，没有信息则用户密码错误
#define LOGIN_USER "select id, score, total_count, win_count from user where usename='%s' and password='%s';"
        char sql[4096] = {0};
        sprintf(sql, LOGIN_USER, user["usename"].asCString(), user["password"].asCString());
        MYSQL_RES *res = NULL;
        {
            std::unique_lock<std::mutex> lock(_mutex);
            bool ret = mysql_util::mysql_exec(_mysql, sql);
            if (ret == false)
            {
                DLOG("user login failed");
                return false;
            }

            // 要么有数据，要么没有，有也只能有一条
            res = mysql_store_result(_mysql);
            if (res == NULL)
            {
                DLOG("have no login user info!");
                return false;
            }
        }

        int row_num = mysql_num_rows(res);
        if (row_num != 1)
        {
            DLOG("the user information queried is not unique!");
            return false;
        }

        MYSQL_ROW row = mysql_fetch_row(res);
        user["id"] = (Json::UInt64)std::stol(row[0]);
        user["score"] = (Json::UInt64)std::stol(row[1]);
        user["total_count"] = std::stoi(row[2]);
        user["win_count"] = std::stoi(row[3]);

        mysql_free_result(res);
        return true;
    }

    // 通过用户名获取用户信息
    bool select_by_name(const std::string &name, Json::Value &user)
    {
#define USER_BY_NAME "select id, score, total_count, win_count from user where usename='%s';"
        char sql[4096] = {0};
        sprintf(sql, USER_BY_NAME, name.c_str());
        MYSQL_RES *res = NULL;
        {
            std::unique_lock<std::mutex> lock(_mutex);
            bool ret = mysql_util::mysql_exec(_mysql, sql);
            if (ret == false)
            {
                DLOG("get user by name failed");
                return false;
            }

            // 要么有数据，要么没有，有也只能有一条
            res = mysql_store_result(_mysql);
            if (res == NULL)
            {
                DLOG("have no login user info!");
                return false;
            }
        }

        int row_num = mysql_num_rows(res);
        if (row_num != 1)
        {
            DLOG("the user information queried is not unique!");
            return false;
        }

        MYSQL_ROW row = mysql_fetch_row(res);
        user["id"] = (Json::UInt64)std::stol(row[0]);
        user["name"] = name;
        user["score"] = (Json::UInt64)std::stol(row[1]);
        user["total_count"] = std::stoi(row[2]);
        user["win_count"] = std::stoi(row[3]);

        mysql_free_result(res);
        return true;
    }

    // 通过id获取用户信息
    bool select_by_id(uint64_t id, Json::Value &user)
    {
#define USER_BY_ID "select usename, score, total_count, win_count from user where id='%d';"
        char sql[4096] = {0};
        sprintf(sql, USER_BY_ID, id);
        MYSQL_RES *res = NULL;
        {
            std::unique_lock<std::mutex> lock(_mutex);
            bool ret = mysql_util::mysql_exec(_mysql, sql);
            if (ret == false)
            {
                DLOG("get user by name failed");
                return false;
            }

            // 要么有数据，要么没有，有也只能有一条
            res = mysql_store_result(_mysql);
            if (res == NULL)
            {
                DLOG("have no login user info!");
                return false;
            }
        }

        int row_num = mysql_num_rows(res);
        if (row_num != 1)
        {
            DLOG("the user information queried is not unique!");
            return false;
        }

        MYSQL_ROW row = mysql_fetch_row(res);
        user["id"] = (Json::UInt64)id;
        user["usename"] = row[0];
        user["score"] = (Json::UInt64)std::stol(row[1]);
        user["total_count"] = std::stoi(row[2]);
        user["win_count"] = std::stoi(row[3]);

        mysql_free_result(res);
        return true;
    }

    // 胜利时天梯分数增加30分，战斗场次增加1，胜利场次增加1
    bool win(uint64_t id)
    {
#define USER_WIN "update user set score=score+30, \
                total_count=total_count+1, win_count=win_count+1 \
                where id='%d';"

        char sql[4096] = {0};
        sprintf(sql, USER_WIN, id);
        MYSQL_RES *res = NULL;
        bool ret = mysql_util::mysql_exec(_mysql, sql);
        if (ret == false)
        {
            DLOG("update win user info failed");
            return false;
        }

        return true;
    }

    // 失败时天梯分数减少30，战斗场次增加1，其他不变
    bool lose(uint64_t id)
    {
#define USER_LOOSE "update user set score=score-3, \
                total_count=total_count+1 where id='%d';"

        char sql[4096] = {0};
        sprintf(sql, USER_LOOSE, id);
        MYSQL_RES *res = NULL;
        bool ret = mysql_util::mysql_exec(_mysql, sql);
        if (ret == false)
        {
            DLOG("update loose user info failed");
            return false;
        }

        return true;
    }
};

#endif